package org.sunrise.game.rpc.node;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.EventLoopGroup;
import lombok.Getter;
import lombok.Setter;
import org.sunrise.game.center.client.ReportClient;
import org.sunrise.game.center.client.ReportClientManager;
import org.sunrise.game.center.message.ReportMessage;
import org.sunrise.game.center.server.NodeManager;
import org.sunrise.game.common.config.ConfigReader;
import org.sunrise.game.common.db.DbService;
import org.sunrise.game.common.db.entity.EntityRpcServerSystem;
import org.sunrise.game.common.log.LogCore;
import org.sunrise.game.common.network.client.BaseClient;
import org.sunrise.game.common.network.client.BaseClientManager;
import org.sunrise.game.common.network.server.BaseServer;
import org.sunrise.game.common.network.server.BaseServerManager;
import org.sunrise.game.common.utils.IdGenerator;
import org.sunrise.game.common.utils.Utils;
import org.sunrise.game.rpc.function.RpcFunction;
import org.sunrise.game.rpc.message.RpcClientMessageManager;
import org.sunrise.game.rpc.message.RpcServerMessageManager;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 一个完整的rpc服务节点
 * 包含：一个rpcServer服务、若干个连接到其他rpc服务的BaseClient、一个reportClient
 * 使用时通过RpcNodeManager.createRpcNode()创建
 */
@Getter
@Setter
public class RpcNode {
    private final int serverId;
    private String ip = null; //绑定ip
    private int port; //绑定端口
    private DbService dbService;

    private EventLoopGroup group = null;
    private Bootstrap b = null;
    private BaseServer rpcServer;
    private Map<Integer, BaseClient> connectToOthers = new ConcurrentHashMap<>();
    private RpcClientMessageManager fromOtherMessageManager = null;

    public RpcNode(int serverId) {
        this.serverId = serverId;
        this.rpcServer = new BaseServer(this.getClass().getSimpleName() + serverId) {
            @Override
            public void onStart() {
                super.onStart();
                if (dbService != null) {
                    dbService.execute("update `rpc_server_system` set `status` = ?, `ip` = ? where `id` = ?", 1, Utils.getLocalIpAddress(), serverId);
                }
            }

            @Override
            public void onStop() {
                if (isStartSuccess()) {
                    super.onStop();
                    if (dbService != null) {
                        dbService.execute("update `rpc_server_system` set `status` = ? where `id` = ?", 0, serverId);
                    }
                }
            }
        };
        rpcServer.setMessageManager(new RpcServerMessageManager(rpcServer.getNodeId()));
        rpcServer.setServerHandler(r -> new RpcServerHandler(rpcServer.getNodeId()));
        BaseServerManager.register(rpcServer);
        IdGenerator.init(NodeManager.NODE_TYPE_RPC_NODE * 100 + serverId);
    }

    public String getNodeId() {
        return rpcServer.getNodeId();
    }

    /**
     * 传入指定端口，进行启动，需手动连接主服
     */
    public void start(int port) {
        setIp(Utils.getListenIpAddress());
        setPort(port);
        rpcServer.startListen(this.ip, this.port);
    }

    /**
     * 传入dbService，进行启动
     * 通过数据表rpc_server_system，保证当前服务的唯一性
     */
    public void start(DbService dbService) {
        this.dbService = dbService;
        int maxPort = 0;
        List<EntityRpcServerSystem> rpcServerSystems = new ArrayList<>();
        try {
            var resultSet = dbService.queryAll("select * from `rpc_server_system`");
            for (Map<String, Object> objectMap : resultSet) {
                rpcServerSystems.add(new EntityRpcServerSystem(objectMap));
            }

            for (var rpcServerSystem : rpcServerSystems) {
                if (rpcServerSystem.getId() == this.serverId) {
                    if (rpcServerSystem.getStatus() == 1) {
                        LogCore.ServerStartUp.error("Server StartUp Failed, name = { RpcServer }, serverId = {}, reason = {}", serverId, "server running");
                        System.exit(-1);
                    } else {
                        setIp(Utils.getListenIpAddress());
                        setPort(rpcServerSystem.getPort());
                    }
                }
                maxPort = Math.max(maxPort, rpcServerSystem.getPort());
            }
            if (ip == null) {
                setIp(Utils.getListenIpAddress());
                setPort(maxPort == 0 ? 20000 : maxPort + 1);
                dbService.execute("insert into `rpc_server_system` (id,ip,port) values (?,?,?)", serverId, Utils.getLocalIpAddress(), port);
            }
            rpcServer.startListen(this.ip, this.port);
            connectMaster();
        } catch (Exception e) {
            LogCore.ServerStartUp.error("Server StartUp Failed, name = { RpcServer }, serverId = {}, reason = {}", serverId, e.getLocalizedMessage());
            System.exit(-1);
        }
    }

    public void otherOffline(int id) {
        BaseClient remove = connectToOthers.remove(id);
        if (remove != null) {
            BaseClientManager.remove(remove.getNodeId());

            // 更新RpcFunction.callIdNodes
            for (List<String> callIdNodes : RpcFunction.callIdNodes.values()) {
                callIdNodes.remove(remove.getServerNodeId());
            }
        }
    }

    public void connectOther(ReportMessage message) {
        BaseClient baseClient = connectToOthers.get(message.getServerId());
        if (baseClient != null) {
            return;
        }
        var connectToOther = new BaseClient(this.getClass().getSimpleName() + serverId + "-ConnectTo-RpcNode" + message.getServerId(), group, b) {
            @Override
            public void onStart() {
                super.onStart();
                for (BaseClient connectToGlobal : connectToOthers.values()) {
                    if (!connectToGlobal.getConnectStatus()) {
                        return;
                    }
                }
            }

            @Override
            public void onFail() {
                super.onFail();
                otherOffline(message.getServerId());
            }
        };

        if (fromOtherMessageManager == null) {
            // 与rpc通信的客户端共用一个消息管理器
            fromOtherMessageManager = new RpcClientMessageManager("");
        }
        connectToOther.setMessageManager(fromOtherMessageManager);
        connectToOther.setClientHandler(r -> new ToOtherRpcNodeHandler(connectToOther.getNodeId()));
        BaseClientManager.register(connectToOther);

        connectToOthers.put(message.getServerId(), connectToOther);
        connectToOther.connect(message.getIp(), message.getPort());
    }

    public void connectMaster() {
        Properties properties = ConfigReader.getProp();
        if (properties == null) {
            return;
        }
        int id = Integer.parseInt(properties.getProperty("master.id"));
        String masterIp = properties.getProperty("master.address");
        int masterPort = Integer.parseInt(properties.getProperty("master.port"));
        String reportIp = properties.getProperty("report.address");

        connectMaster(masterIp, masterPort, reportIp);
    }

    public void connectMaster(String masterIp, int masterPort, String reportIp) {
        var reportClient = ReportClientManager.createReportClient(rpcServer.getNodeId() + "-ReportClient", NodeManager.NODE_TYPE_RPC_NODE, serverId, reportIp, port);
        reportClient.getConnectToCenter().setMessageManager(new RpcNodeReportMessageManager(reportClient.getConnectToCenter().getNodeId()));
        reportClient.connectMaster(masterIp, masterPort);
    }
}
